package service

import (
	"bloggo/bloggo"
	"bloggo/dto"
	"bloggo/structs"
	"context"
	"errors"
	"fmt"
	"gitee.com/chunanyong/logger"
	"gitee.com/chunanyong/zorm"
	"github.com/gin-gonic/gin"
	"strconv"
)

//SaveTbArticleStruct 保存
//如果入参ctx中没有dbConnection,使用defaultDao开启事务并最后提交
//如果入参ctx有dbConnection且没有事务,调用dbConnection.begin()开启事务并最后提交
//如果入参ctx有dbConnection且有事务,只使用不提交,有开启方提交事务
//但是如果遇到错误或者异常,虽然不是事务的开启方,也会回滚事务,让事务尽早回滚
func SaveTbArticleStruct(ctx context.Context, tbarticlestruct *structs.TbArticleStruct) error {

	// tbarticlestruct对象指针不能为空
	if tbarticlestruct == nil {
		return errors.New("tbarticlestruct对象指针不能为空")
	}
	//匿名函数return的error如果不为nil,事务就会回滚
	_, errSaveTbArticleStruct := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

		//事务下的业务代码开始

		_, errSaveTbArticleStruct := zorm.Insert(ctx, tbarticlestruct)

		if errSaveTbArticleStruct != nil {
			return nil, errSaveTbArticleStruct
		}

		return nil, nil
		//事务下的业务代码结束

	})

	//记录错误
	if errSaveTbArticleStruct != nil {
		errSaveTbArticleStruct := fmt.Errorf("service.SaveTbArticleStruct错误:%w", errSaveTbArticleStruct)
		logger.Error(errSaveTbArticleStruct)
		return errSaveTbArticleStruct
	}

	return nil
}

//UpdateTbArticleStruct 更新
//如果入参ctx中没有dbConnection,使用defaultDao开启事务并最后提交
//如果入参ctx有dbConnection且没有事务,调用dbConnection.begin()开启事务并最后提交
//如果入参ctx有dbConnection且有事务,只使用不提交,有开启方提交事务
//但是如果遇到错误或者异常,虽然不是事务的开启方,也会回滚事务,让事务尽早回滚
func UpdateTbArticleStruct(ctx context.Context, tbarticlestruct *structs.TbArticleStruct) error {

	// tbarticlestruct对象指针或主键Id不能为空
	if tbarticlestruct == nil || tbarticlestruct.Id < 1 {
		return errors.New("tbarticlestruct对象指针或主键Id不能为空")
	}

	//匿名函数return的error如果不为nil,事务就会回滚
	_, errUpdateTbArticleStruct := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

		//事务下的业务代码开始
		_, errUpdateTbArticleStruct := zorm.Update(ctx, tbarticlestruct)

		if errUpdateTbArticleStruct != nil {
			return nil, errUpdateTbArticleStruct
		}

		return nil, nil
		//事务下的业务代码结束

	})

	//记录错误
	if errUpdateTbArticleStruct != nil {
		errUpdateTbArticleStruct := fmt.Errorf("service.UpdateTbArticleStruct错误:%w", errUpdateTbArticleStruct)
		logger.Error(errUpdateTbArticleStruct)
		return errUpdateTbArticleStruct
	}

	return nil
}

//UpdateNotZeroTbArticleStruct 更新
//如果入参ctx中没有dbConnection,使用defaultDao开启事务并最后提交
//如果入参ctx有dbConnection且没有事务,调用dbConnection.begin()开启事务并最后提交
//如果入参ctx有dbConnection且有事务,只使用不提交,有开启方提交事务
//但是如果遇到错误或者异常,虽然不是事务的开启方,也会回滚事务,让事务尽早回滚
func UpdateNotZeroTbArticleStruct(ctx context.Context, tbarticlestruct *structs.TbArticleStruct) error {

	// tbarticlestruct对象指针或主键Id不能为空
	if tbarticlestruct == nil || tbarticlestruct.Id < 1 {
		return errors.New("tbarticlestruct对象指针或主键Id不能为空")
	}

	//匿名函数return的error如果不为nil,事务就会回滚
	_, errUpdateTbArticleStruct := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

		//事务下的业务代码开始
		_, errUpdateTbArticleStruct := zorm.UpdateNotZeroValue(ctx, tbarticlestruct)

		if errUpdateTbArticleStruct != nil {
			return nil, errUpdateTbArticleStruct
		}

		return nil, nil
		//事务下的业务代码结束

	})

	//记录错误
	if errUpdateTbArticleStruct != nil {
		errUpdateTbArticleStruct := fmt.Errorf("service.UpdateNotZeroTbArticleStruct错误:%w", errUpdateTbArticleStruct)
		logger.Error(errUpdateTbArticleStruct)
		return errUpdateTbArticleStruct
	}

	return nil
}

//DeleteTbArticleStructById 根据Id删除
//如果入参ctx中没有dbConnection,使用defaultDao开启事务并最后提交
//如果入参ctx有dbConnection且没有事务,调用dbConnection.begin()开启事务并最后提交
//如果入参ctx有dbConnection且有事务,只使用不提交,有开启方提交事务
//但是如果遇到错误或者异常,虽然不是事务的开启方,也会回滚事务,让事务尽早回滚
func DeleteTbArticleStructById(ctx context.Context, id string) error {

	//id不能为空
	if len(id) < 1 {
		return errors.New("id不能为空")
	}

	//匿名函数return的error如果不为nil,事务就会回滚
	_, errDeleteTbArticleStruct := zorm.Transaction(ctx, func(ctx context.Context) (interface{}, error) {

		//事务下的业务代码开始
		finder := zorm.NewDeleteFinder(structs.TbArticleStructTableName).Append(" WHERE id=?", id)
		_, errDeleteTbArticleStruct := zorm.UpdateFinder(ctx, finder)

		if errDeleteTbArticleStruct != nil {
			return nil, errDeleteTbArticleStruct
		}

		return nil, nil
		//事务下的业务代码结束

	})

	//记录错误
	if errDeleteTbArticleStruct != nil {
		errDeleteTbArticleStruct := fmt.Errorf("service.DeleteTbArticleStruct错误:%w", errDeleteTbArticleStruct)
		logger.Error(errDeleteTbArticleStruct)
		return errDeleteTbArticleStruct
	}

	return nil
}

//FindTbArticleStructById 根据Id查询信息
//ctx中如果没有dbConnection,则会使用默认的datasource进行无事务查询
func FindTbArticleStructById(ctx context.Context, id string) (*structs.TbArticleStruct, error) {
	//id不能为空
	if len(id) < 1 {
		return nil, errors.New("id不能为空")
	}

	//根据Id查询
	finder := zorm.NewSelectFinder(structs.TbArticleStructTableName).Append(" WHERE id=?", id)
	tbarticlestruct := structs.TbArticleStruct{}
	has, errFindTbArticleStructById := zorm.QueryRow(ctx, finder, &tbarticlestruct)

	//记录错误
	if errFindTbArticleStructById != nil {
		errFindTbArticleStructById := fmt.Errorf("service.FindTbArticleStructById错误:%w", errFindTbArticleStructById)
		logger.Error(errFindTbArticleStructById)
		return nil, errFindTbArticleStructById
	}

	if !has {
		return nil, nil
	}

	return &tbarticlestruct, nil

}

//FindTbArticleStructList 根据Finder查询列表
//ctx中如果没有dbConnection,则会使用默认的datasource进行无事务查询
func FindTbArticleStructList(ctx context.Context, finder *zorm.Finder, page *zorm.Page) ([]structs.TbArticleStruct, error) {

	//finder不能为空
	if finder == nil {
		return nil, errors.New("finder不能为空")
	}

	tbarticlestructList := make([]structs.TbArticleStruct, 0)
	errFindTbArticleStructList := zorm.Query(ctx, finder, &tbarticlestructList, page)

	//记录错误
	if errFindTbArticleStructList != nil {
		errFindTbArticleStructList := fmt.Errorf("service.FindTbArticleStructList错误:%w", errFindTbArticleStructList)
		logger.Error(errFindTbArticleStructList)
		return nil, errFindTbArticleStructList
	}

	return tbarticlestructList, nil
}

// FindListRecommendArticles 查询推荐文章
func FindListRecommendArticles(ctx context.Context, articleId string, data *dto.ArticleDetailInfo) {
	finder := zorm.NewFinder().Append("SELECT id,article_title,article_cover,create_time FROM").Append(structs.TbArticleStructTableName)
	finder.Append("WHERE is_delete = ? AND status = ?", 0, dto.PUBLIC).Append("AND id IN (SELECT article_id FROM tb_article_tag WHERE tag_id IN (SELECT tag_id FROM tb_article_tag WHERE article_id =?) AND article_id !=?)", articleId, articleId)
	finder.Append("ORDER BY is_top DESC, id DESC LIMIT ?", 6)
	err := zorm.Query(ctx, finder, &data.RecommendArticleList, nil)
	if err != nil {
		bloggo.Log(err.Error())
	}
}

// FindNewestArticleList 查询最新文章
func FindNewestArticleList(ctx context.Context, articleId string, data *dto.ArticleDetailInfo) {
	finder := zorm.NewFinder().Append("SELECT id,article_title,article_cover,create_time FROM tb_article")
	finder.Append("WHERE is_delete = ? AND status = ? AND id != ? ORDER BY id DESC LIMIT ?", 0, dto.PUBLIC, articleId, 5)
	err := zorm.Query(ctx, finder, &data.NewestArticleList, nil)
	if err != nil {
		bloggo.Log(err.Error())
	}
}

// UpdateArticleViewsCount 更新文章浏览量
func UpdateArticleViewsCount(ctx context.Context, articleId string) {
	c := ctx.(*gin.Context)
	address, err := bloggo.GetIpAddress(c.Request)
	if err != nil {
		bloggo.Log(err.Error())
	}
	key := bloggo.ARTICLE_SET + ":" + address
	if bloggo.SisMember(key, []byte(articleId)) {
		return
	}
	bloggo.Sadd(key, []byte(articleId))
	bloggo.Expire(key, 60*60*24) //24小时过期时间
	//文章浏览量+1
	bloggo.Zincrby(bloggo.ARTICLE_VIEWS_COUNT, 1, []byte(articleId))
}

// GenIdNotExistTInTable 生成一个表中不存在的主键id
func GenIdNotExistTInTable(id *string) bool {
	finder := zorm.NewSelectFinder(structs.TbArticleStructTableName, "MAX(id)")
	var maxId int
	if len(*id) == 0 {
		zorm.QueryRow(context.Background(), finder, &maxId)
		maxId++
		*id = strconv.Itoa(maxId)
		return false
	} else {
		finder.Append("WHERE id=?", id)
		row, _ := zorm.QueryRow(context.Background(), finder, &maxId)
		if row && maxId > 0 {
			return true
		}
		return false
	}
}

func FindMaxSrotno() (maxSortno int) {
	finder := zorm.NewSelectFinder(structs.TbArticleStructTableName, "MAX(sortno)")
	row, _ := zorm.QueryRow(context.Background(), finder, &maxSortno)
	if row {
		return maxSortno
	}
	return 0
}
